<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.2.8: http://docutils.sourceforge.net/" />
<title>O que há de novo no Python2.2: Metaclass Programming</title>
<link rel="stylesheet" href="default.css" type="text/css" />
</head>
<body>
<div class="document" id="o-que-h-de-novo-no-python2-2-metaclass-programming">
<h1 class="title">O que há de novo no Python2.2: Metaclass Programming</h1>
<h2 class="subtitle" id="o-workshop-brasileiro-de-zope-e-python">1o. Workshop Brasileiro de Zope e Python</h2>
<table class="docinfo" frame="void" rules="none">
<col class="docinfo-name" />
<col class="docinfo-content" />
<tbody valign="top">
<tr class="field"><th class="docinfo-name">Autor:</th><td class="field-body">Sidnei da Silva</td>
</tr>
<tr class="field"><th class="docinfo-name">Contato:</th><td class="field-body"><a class="reference" href="mailto:sidnei&#64;x3ng.com">sidnei&#64;x3ng.com</a></td>
</tr>
<tr class="field"><th class="docinfo-name">Versão:</th><td class="field-body">1.4</td>
</tr>
<tr class="field"><th class="docinfo-name">Data:</th><td class="field-body">2003-06-04</td>
</tr>
</tbody>
</table>
<div class="section" id="metaclasses">
<h1><a name="metaclasses">Metaclasses</a></h1>
<p>O conceito de metaclasse existe desde tempos remotos (anterior ao
Python 1.5), mas nunca foi muito explorado, pois apesar de muito
poderoso, sofria por ter uma implementação obscura e pouco amigável de
ser programada. Essa funcionalidade era conhecida como &quot;the Don
Beaudry hook&quot;, levando o nome de seu inventor e principal
utilizador. Os casos mais conhecidos de utilizacão do &quot;Don Beaudry
hook&quot; são o trabalho do próprio Don Beaudry, no seu pacote <tt class="literal"><span class="pre">MESS</span></tt> e o
trabalho de Jim Fulton nas <tt class="literal"><span class="pre">Extension</span> <span class="pre">Classes</span></tt>, que continua em uso
hoje em dia no Zope 2.x.</p>
<p>Técnicamente falando, uma metaclasse não é nada mais do que &quot;a classe
de uma classe&quot;. Qualquer classe cuja instância é uma classe, é
considerada uma metaclasse. Quando falamos de uma instância que não é
uma classe, a metaclasse dessa instância é a classe de sua classe. Por
definição: a metaclasse de um objeto <tt class="literal"><span class="pre">x</span></tt> é
<tt class="literal"><span class="pre">x.__class__.__class__</span></tt>, porém quando falamos de uma classe <tt class="literal"><span class="pre">C</span></tt>,
quando nos referimos à sua metaclasse, estamos nos referindo a
<tt class="literal"><span class="pre">C.__class__</span></tt>, e não <tt class="literal"><span class="pre">C.__class__.__class__</span></tt>, que seria sua meta-metaclasse.</p>
<p>A metaclasse mais comum é o built-in <tt class="literal"><span class="pre">type</span></tt>. Ela é a classe base de
todos os tipos built-in. As classes clássicas usam uma metaclasse
diferente: <tt class="literal"><span class="pre">types.ClassType</span></tt>, entretanto, não é possivel acessar a
metaclasse de uma classe clássica utilizando
<tt class="literal"><span class="pre">x.__class__.__class__</span></tt>: é preciso utilizar <tt class="literal"><span class="pre">type(x.__class__)</span></tt>,
pois as classes clássicas nao supportam o atributo <tt class="literal"><span class="pre">__class__</span></tt> em
classes (este atributo so esta disponível em instâncias).</p>
<p>Quando o interpretador encontra uma declaração de uma classe, ele
primeiro determina qual a metaclasse <tt class="literal"><span class="pre">M</span></tt> será a metaclasse da classe
que está sendo criada, e em seguida, o interpretador executa <tt class="literal"><span class="pre">M(name,</span>
<span class="pre">bases,</span> <span class="pre">dict)</span></tt>, onde <tt class="literal"><span class="pre">name</span></tt> é o nome da classe que será criada,
<tt class="literal"><span class="pre">bases</span></tt> é uma tupla contendo as classes base, e <tt class="literal"><span class="pre">dict</span></tt> é um
dicionário contendo os metodos e variáveis de classe definidas na
declaração da classe. Isso tudo é executado depois do corpo da classe
(onde os métodos e variáveis são definidos). O resultado da chamada é
então é setado huma variável correspondente ao nome da classe.</p>
<div class="section" id="como-a-metaclasse-m-determinada">
<h2><a name="como-a-metaclasse-m-determinada">Como a metaclasse M é determinada?</a></h2>
<blockquote>
<ul class="simple">
<li>Se houver uma propriedade <tt class="literal"><span class="pre">__metaclass__</span></tt> na classe, ela é usada.</li>
<li>Senão, se houver pelo menos uma classe base, a busca é feita na
classe base. <a class="footnote-reference" href="#id2" id="id1" name="id1"><sup>1</sup></a></li>
<li>Senão, se houver uma variável global ao módulo com o nome
<tt class="literal"><span class="pre">__metaclass__</span></tt>, essa variável é utilizada.</li>
<li>Senão a metaclasse clássica (types.ClassType) é utilizada.</li>
</ul>
</blockquote>
<table class="footnote" frame="void" id="id2" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#id1" name="id2">[1]</a></td><td>Primeiro é buscado o atributo <tt class="literal"><span class="pre">__class__</span></tt>, e se
ele não existir, é usado o tipo da classe. Esse recurso (conhecido
como <tt class="literal"><span class="pre">the</span> <span class="pre">Don</span> <span class="pre">Beaudry</span> <span class="pre">hook</span></tt>) existia também no python clássico,
mas era apenas executado quando a metaclasse fosse executável.</td></tr>
</tbody>
</table>
<p>O caso mais comum é M ser ou <tt class="literal"><span class="pre">types.ClassType</span></tt>, o que gera uma classe
clássica, ou <tt class="literal"><span class="pre">type</span></tt>, o que gera uma <tt class="literal"><span class="pre">new-style</span> <span class="pre">class</span></tt>. Outros
casos comuns são a utilização de uma extensão em C como metaclasse
(por exemplo, a ExtensionClass encontrada no Zope), ou um subtipo de
<tt class="literal"><span class="pre">type</span></tt>.</p>
<p>As exceções na determinação da metaclasse de uma classe são duas:</p>
<blockquote>
<ol class="arabic simple">
<li>Se você misturar classes clássicas e <tt class="literal"><span class="pre">new-style</span> <span class="pre">classes</span></tt> como
classes bases, a metaclasse da primeira new-style class é
utilizada ao invés de <tt class="literal"><span class="pre">types.ClassType</span></tt>. O efeito disso é que se você
misturar classes clássicas e new-style, o resultado será uma classe
new-style.</li>
<li>Para classes new-style, há uma restrição de que a metaclasse
escolhida seja igual a ou uma subclasse de cada uma das metaclasses
das classes base. <a class="footnote-reference" href="#id4" id="id3" name="id3"><sup>2</sup></a></li>
</ol>
</blockquote>
<table class="footnote" frame="void" id="id4" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#id3" name="id4">[2]</a></td><td>Considere uma classe <tt class="literal"><span class="pre">C</span></tt>, com duas classes base <tt class="literal"><span class="pre">B``1</span> <span class="pre">e</span>
<span class="pre">``B2</span></tt>. Digamos que <tt class="literal"><span class="pre">M</span> <span class="pre">=</span> <span class="pre">C.__class__</span></tt>, <tt class="literal"><span class="pre">M1</span> <span class="pre">=</span> <span class="pre">B1.__class__</span></tt>,
<tt class="literal"><span class="pre">M2</span> <span class="pre">=</span> <span class="pre">B2.__class__</span></tt>. Então, o requerimento para a metaclass M é
determinado por <tt class="literal"><span class="pre">issubclass(M,</span> <span class="pre">M1)</span> <span class="pre">and</span> <span class="pre">issubclass(M,</span> <span class="pre">M2)</span></tt>. Isso
ocorre pois um método de <tt class="literal"><span class="pre">B1</span></tt> deve poder usar um meta-método
definido em <tt class="literal"><span class="pre">M1</span></tt> em <tt class="literal"><span class="pre">self.__class__</span></tt>, mesmo quando <tt class="literal"><span class="pre">self</span></tt> é
uma instância de uma subclasse de <tt class="literal"><span class="pre">B1</span></tt>. No livro <tt class="literal"><span class="pre">Putting</span>
<span class="pre">metaclasses</span> <span class="pre">to</span> <span class="pre">work</span></tt>, é descrito um mecanismo pelo qual uma
metaclasse é automaticamente criada por herança multipla a partir
das classes <tt class="literal"><span class="pre">M1</span></tt> e <tt class="literal"><span class="pre">M2</span></tt>, porém no Python 2.2 foi escolhido um
método mais simples, onde uma exceção ocorre se este requerimento
não for satisfeito.</td></tr>
</tbody>
</table>
</div>
</div>
<div class="section" id="exemplos-de-metaclasses">
<h1><a name="exemplos-de-metaclasses">Exemplos de metaclasses</a></h1>
<p>Vamos recapitular um pouco do que foi visto. Lembra que a declaração
de uma classe resulta em uma chamada a <tt class="literal"><span class="pre">M(name,</span> <span class="pre">bases,</span> <span class="pre">dict)</span></tt> onde
<tt class="literal"><span class="pre">M</span></tt> é a metaclasse. Agora, a metaclasse é uma classe, e quando uma
classe é executada, seus métodos <tt class="literal"><span class="pre">__new__</span></tt> e <tt class="literal"><span class="pre">__init__</span></tt> são
chamados em seqüencia, de modo que o acontece é equivalente a:</p>
<pre class="literal-block">
cls = M.__new__(M, name, bases, dict)
assert cls.__class__ is M
M.__init__(cls, name, bases, dict)
</pre>
<p>Note que a chamada do método <tt class="literal"><span class="pre">__init__</span></tt> é feita como um método
<tt class="literal"><span class="pre">unbound</span></tt> aqui. Isto foi feito para deixar claro que estamos
chamando o método <tt class="literal"><span class="pre">__init__</span></tt> definido por <tt class="literal"><span class="pre">M</span></tt>, e não o
<tt class="literal"><span class="pre">__init__</span></tt> definido pela classe (que é utilizado na inicialização
das instâncias de <tt class="literal"><span class="pre">cls</span></tt>.</p>
<p>Agora vamos a um exemplo prático. Vamos ver como utilizar metaclasses
para geração dinâmica de métodos de uma classe e propriedades. Vamos
criar duas metaclasses: uma irá gerar os métodos <tt class="literal"><span class="pre">getXXX</span></tt> e
<tt class="literal"><span class="pre">setXXX</span></tt>, e a outra irá utilizar o descriptor <tt class="literal"><span class="pre">property</span></tt> (novo no
Python 2.2) para setar a propriedade utilizando os métodos criados
pela primeira metaclasse.</p>
<p>Vamos começar pela definição da classe <tt class="literal"><span class="pre">field</span></tt>, que irá conter as
configurações para inicialização das propriedades. Essa classe vai ser
extremamente simples, para fins de exemplificação:</p>
<pre class="literal-block">
class field:
  def __init__(self, name):
      self.__name__ = name

  def getName(self):
      return self.__name__
</pre>
<p>Agora vamos criar a primeira metaclasse, que irá gerar os métodos da
classe:</p>
<pre class="literal-block">
class generator(type):
      def __init__(cls, name, bases, dict):
          super(autoprop, cls).__init__(name, bases, dict)
          props = {}
          for item in dict.values():
              if isinstance(item, field):
                 name = item.getName()
                 self.generateAccessor(cls, name)
                 self.generateMutator(cls, name)

      def generateAccessor(self, cls, name):
          method = lambda self, name=name: \
                   getattr(self, name)
          methodName = &quot;get%s&quot; % name.capitalize()
          setattr(cls, methodName, method)

      def generateMutator(self, cls, name):
          method = lambda self, value, name=name: \
                   setattr(self, name, value)
          methodName = &quot;set%s&quot; % name.capitalize()
          setattr(cls, methodName, method)
</pre>
<p>E em seguida a classe que irá gerar as propriedades usando o
descriptor <tt class="literal"><span class="pre">property</span></tt>:</p>
<pre class="literal-block">
class autoprop(type):
    def __init__(cls, name, bases, dict):
        super(autoprop, cls).__init__(name, bases, dict)
        props = {}
        for item in dict.values():
            if isinstance(item, field):
               name = item.getName()
               props[name] = 1
        for name in props.keys():
            fget = getattr(cls, &quot;get%s&quot; % \
                   name.capitalize(), None)
            fset = getattr(cls, &quot;set%s&quot; % \
                   name.capitalize(), None)
            setattr(cls, name, property(fget, fset))
</pre>
<p>Agora podemos utilizar as duas classes em conjunto, pois utilizamos
<tt class="literal"><span class="pre">super</span></tt> no método <tt class="literal"><span class="pre">__init__</span></tt> para garantir que a subclasse irá
chamar o método da classe base antes de executar seu próprio método. O
resultado fica assim:</p>
<pre class="literal-block">
class autogen(generator, autoprop):
      pass
</pre>
<p>E para utilizar nossa metaclasse:</p>
<pre class="literal-block">
class MyClass:
      __metaclass__ = autogen

      firstname = field('firstname')
      lastname  = field('lastname')
</pre>
<p>Por hoje é isso. Se você estiver interessado no código completo deste
exemplo, ele pode ser encontrado <a class="reference" href="autogen.py">aqui</a>.</p>
</div>
<div class="section" id="utiliza-o-pr-tica">
<h1><a name="utiliza-o-pr-tica">Utilização prática</a></h1>
<p>Partes do código do Zope 3 utilizam um recurso bastante parecido com
esse, além do mais, há um produto conhecido como <tt class="literal"><span class="pre">Archetypes</span></tt> que
usa o conceito de geração automática de classes baseadas em
<tt class="literal"><span class="pre">schema</span></tt>, mas devido à necessidade de manter compatibilidade com
Python 2.1, a sua implementação ainda não utiliza metaclasses. Assim
que o Zope 2.x suportar oficialmente Python 2.2, iremos reescrever o
Archetypes para usar metaclasses e descriptors.</p>
<p>A última versão deste documento pode ser encontrada no <a class="reference" href="http://cvs.x3ng.com.br/cgi-bin/viewcvs.cgi/talks/sidnei/fisl2003/metaclasses/">CVS da X3ng</a>.</p>
</div>
<div class="section" id="refer-ncias">
<h1><a name="refer-ncias">Referências</a></h1>
<blockquote>
<ul class="simple">
<li><a class="reference" href="http://www.python.org/2.2.3/descrintro.html">Unifying types and classes in Python 2.2</a>, by Guido Van Rossum</li>
<li><a class="reference" href="http://www.python.org/peps/pep-0252.html">PEP 252</a> - Making Types Look More Like Classes</li>
<li><a class="reference" href="http://www.python.org/peps/pep-0253.html">PEP 253</a> - Subtyping Built-in Types</li>
<li><a class="reference" href="http://www.python.org/doc/essays/metaclasses/">Metaclasses in Python 1.5</a> - A.k.a. The Killer Joke</li>
</ul>
</blockquote>
</div>
</div>
</body>
</html>
